/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hxg.portmgr.core.oper.snmp;

import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import hxg.portmgr.core.oper.snmp.oidgroup.FlowOID;
import hxg.portmgr.core.oper.snmp.oidgroup.OidEntry;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;

/**
 * SNMP对象的设备的实例,有一个IP地址,一个设备类型的OID,一个接口的MAP组成,简单封装
 *
 * @author hxg
 */
@Deprecated
public class SnmpEntity {

    final static Logger logger = Logger.getLogger(SnmpEntity.class.getName());
    String ip;
    int port;
    String type_oid;
    //Map<String, SnmpIf> ifmp = new HashMap<String, SnmpIf>();
    CommunityTarget target = new CommunityTarget();
    Snmp snmp = null;
    int pdutype = PDU.GETNEXT;
    int version = SnmpConstants.version1;
    TransportMapping transport = null;

    public SnmpEntity(String ip, int port, String community) {
        this.ip = ip;
        this.port = port;
        Address targetAddress = GenericAddress.parse("udp:" + ip + "/" + port);
        target.setCommunity(new OctetString(community));
        target.setAddress(targetAddress);
        target.setTimeout(10000);
        target.setVersion(SnmpConstants.version2c);
        try {
            transport = new DefaultUdpTransportMapping();
            transport.listen();
            snmp = new Snmp(transport);
        } catch (IOException ex) {
            //System.out.println("创建失败^_^!");
            logger.log(Level.SEVERE, "创建失败^_^!", ex);
        }
    }

    public static Snmp getSnmp(String ip, int port, String community, int timeout, int version) throws IOException {
        Address targetAddress = GenericAddress.parse("udp:" + ip + "/" + port);
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(community));
        target.setAddress(targetAddress);
        target.setTimeout(timeout);
        target.setVersion(version);
        TransportMapping transport = new DefaultUdpTransportMapping();
        transport.listen();
        Snmp snmp = new Snmp(transport);
        return snmp;
    }

    public static Snmp getSnmp(String ip, String community) throws IOException {
        return getSnmp(ip, 161, community, 10000, SnmpConstants.version2c);
    }

    public static Snmp getSnmp(String ip) throws IOException {
        return getSnmp(ip, 161, "public", 10000, SnmpConstants.version2c);
    }

    public boolean isSupportAble() {
        PDU pdu = new PDU();
        pdu.setType(PDU.GETNEXT);
        pdu.add(new VariableBinding(new OID("1.3.6")));
        pdu = send(pdu);
        if (pdu != null && pdu.size() > 0) {
            return true;
        } else {
            logger.log(Level.INFO, "IP[{0}]不可达", ip);
            return false;
        }
    }

    private PDU send(final PDU pdu) {
        try {
            ResponseEvent response = snmp.send(pdu, target);
            if (response != null) {
                PDU recv_pdu = response.getResponse();
                return recv_pdu;
            }
            return null;
        } catch (IOException ex) {
            logger.log(Level.SEVERE, null, ex);
            return null;
        }
    }

    public void close() {
        try {
            transport.close();
            snmp.close();
        } catch (IOException ex) {
            logger.log(Level.SEVERE, null, ex);
        }

    }

    public boolean isClosed() {
        return !transport.isListening();
    }

    public void setTimeout(int timeout) {
        target.setTimeout(timeout);
    }

    public void setCommunity(String Community) {
        target.setCommunity(new OctetString(Community));
    }

    public void setVersion(int version) {
        target.setVersion(version);
    }

    public String get(OID oid) {
        PDU pdu = new PDU();
        pdu.setType(PDU.GET);
        pdu.add(new VariableBinding(oid));
        pdu = send(pdu);
        if (pdu != null && pdu.size() > 0) {
            String val = pdu.get(0).getVariable().toString().trim();
            logger.log(Level.FINE, "发送：{0};接收到：{1}", new Object[]{oid, val});
            return val;
        } else {
            logger.log(Level.FINE, "发送：{0};未接收到数据", new Object[]{oid});
            return "";
        }
    }

    public String get(String oid) {
        PDU pdu = new PDU();
        pdu.setType(PDU.GET);
        pdu.add(new VariableBinding(new OID(oid)));
        pdu = send(pdu);
        if (pdu != null && pdu.size() > 0) {
            String val = pdu.get(0).getVariable().toString().trim();
            logger.log(Level.FINE, "发送：{0};接收到：{1}", new Object[]{oid, val});
            return val;
        } else {
            logger.log(Level.FINE, "发送：{0};未接收到数据", new Object[]{oid});
            return "";
        }
    }

    private Map<String, String> get(OID[] oids) {
        Map<String, String> mp = new LinkedHashMap();
        PDU pdu = new PDU();
        pdu.setType(PDU.GET);
        for (OID oid : oids) {
            pdu.addOID(new VariableBinding(oid));
        }
        try {
            ResponseEvent response = snmp.send(pdu, target);
            if (response != null) {
                PDU recv_pdu = response.getResponse();
                if (recv_pdu != null && recv_pdu.size() > 0) {
                    for (int i = 0; i < recv_pdu.size(); i++) {
                        String key = recv_pdu.get(i).getOid().toString().trim();
                        String val = recv_pdu.get(i).getVariable().toString().trim();
                        logger.log(Level.FINE, "发送：{0};接收到：{1}", new Object[]{key, val});
                        mp.put(key, val);
                    }
                }
            }
        } catch (IOException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
        return mp;
    }

    /**
     * 核心实现
     *
     * @param oids
     * @param maxsize 一个PDU最大封装OID的数量
     * @return
     */
    public Map<String, String> get(OID[] oids, int maxsize) {
        Map<String, String> mp = new LinkedHashMap();
        int i = 0;
        ArrayList<OID> oidlist = new ArrayList();
        for (OID oid : oids) {
            oidlist.add(oid);
            i++;
            if (i >= maxsize) {
                OID[] oidsend = new OID[oidlist.size()];
                oidsend = oidlist.toArray(oidsend);
                i = i - maxsize;
                mp.putAll(get(oidsend));
                oidlist = new ArrayList();
            }
        }
        OID[] oidsend = new OID[oidlist.size()];
        oidsend = oidlist.toArray(oidsend);
        mp.putAll(get(oidsend));
        return mp;
    }

    public Map<String, String> get(String[] oids, int maxsize) {
        OID[] ods = new OID[oids.length];
        for (int i = 0; i < ods.length; i++) {
            ods[i] = new OID(oids[i]);
        }
        return get(ods, maxsize);
    }

    public Map<String, String> get(Collection<String> oids, int maxsize) {
        String[] arrayoids = new String[oids.size()];
        int i = 0;
        for (String oid : oids) {
            arrayoids[i] = oid;
            i++;
        }
        return get(arrayoids, maxsize);
    }

    /**
     * 获取连续定义于OidGroup内的OID组的值，必须是无须Index的
     *
     * @param oidGroups
     * @return
     */
    public <T extends OidEntry> Map<T, String> getMapOfGroupOids(T[] oidGroups) throws IOException {
        return getMapOfGroupOids(null, oidGroups);
    }

    /**
     * 获取连续定义于OidGroup内的OID组的值，必须是需Index的
     *
     * @param oidGroups
     * @return
     */
    public <T extends OidEntry> Map<T, String> getMapOfGroupOids(String ifidx, T[] oidGroups) throws IOException {
        Map<T, String> mp = new HashMap();
        PDU pdu = new PDU();
        pdu.setType(PDU.GET);
        Map<String, T> oid2flowoidmp = new HashMap();
        for (T oidGroup : oidGroups) {
            OID oid = (ifidx == null || ifidx.isEmpty()) ? new OID(oidGroup.oidValue()) : new OID(oidGroup.oidValue() + "." + ifidx);
            System.out.println("oid=" + oid);
            pdu.addOID(new VariableBinding(oid));
            oid2flowoidmp.put(oid.toString(), oidGroup);
        }
        ResponseEvent response = snmp.send(pdu, target);
        if (response != null) {
            PDU recv_pdu = response.getResponse();
            if (recv_pdu != null && recv_pdu.size() > 0) {
                for (int i = 0; i < recv_pdu.size(); i++) {
                    try {
                        String key = recv_pdu.get(i).getOid().toString().trim();
                        String val = recv_pdu.get(i).getVariable().toString();
                        logger.log(Level.FINE, "发送：{0};接收到：{1}", new Object[]{key, val});
                        mp.put(oid2flowoidmp.get(key), val);
                    } catch (NumberFormatException e) {
                        logger.log(Level.WARNING, e.getMessage());
                    }
                }
            }
        }
        return mp;
    }

    public Map<OidEntry, String> getMapOfGroupOids(String ifidx, Collection<? extends OidEntry> oidGroups) throws IOException {
        OidEntry[] flowoidarrs = new FlowOID[oidGroups.size()];
        flowoidarrs = oidGroups.toArray(flowoidarrs);
        return getMapOfGroupOids(ifidx, flowoidarrs);
    }

    public EnumMap<FlowOID, Long> getMapOfFlowOids(String ifidx, FlowOID[] flowoids) throws IOException {
        EnumMap<FlowOID, Long> mp = new EnumMap(FlowOID.class);
        PDU pdu = new PDU();
        pdu.setType(PDU.GET);
        HashMap<String, FlowOID> oid2flowoidmp = new HashMap();
        for (FlowOID foid : flowoids) {

            OID oid = new OID(foid.oidValue() + "." + ifidx);
            pdu.addOID(new VariableBinding(oid));
            oid2flowoidmp.put(oid.toString(), foid);
        }
        ResponseEvent response = snmp.send(pdu, target);
        if (response != null) {
            PDU recv_pdu = response.getResponse();
            if (recv_pdu != null && recv_pdu.size() > 0) {
                for (int i = 0; i < recv_pdu.size(); i++) {
                    try {
                        String key = recv_pdu.get(i).getOid().toString().trim();
                        Long val = recv_pdu.get(i).getVariable().toLong();
                        logger.log(Level.FINE, "发送：{0};接收到：{1}", new Object[]{key, val});
                        mp.put(oid2flowoidmp.get(key), val);
                    } catch (NumberFormatException e) {
                        logger.log(Level.WARNING, e.getMessage());
                    }
                }
            }
        }

        return mp;
    }

    public EnumMap<FlowOID, Long> getMapOfFlowOids(String ifidx, Collection<FlowOID> flowoids) throws IOException {
        FlowOID[] flowoidarrs = new FlowOID[flowoids.size()];
        flowoidarrs = flowoids.toArray(flowoidarrs);
        return getMapOfFlowOids(ifidx, flowoidarrs);
    }

    /**
     *
     * 与Map<String, String> get(List<String>
     * oids)方法一致,但是保证顺序与oids一致,如果指定位置的oid值对应value为空，则在该对应的位置值为NULL
     * 与SNMPLOG区别在于这个没有包装时间和IP，不适合发送、接收和处理的流水线作业 maxsize
     * 表示一次批量获取的值,如果oids的数组大于maxsize,则会分多次向设备获取
     *
     * @param oids
     * @return
     */
    public String[] getRemoteValues(String[] oids, int maxsize) {
        Map<String, String> mp = this.get(oids, maxsize);
        String[] values = new String[oids.length];
        for (int i = 0; i < oids.length; i++) {
            if (mp.containsKey(oids[i])) {
                values[i] = mp.get(oids[i]);
            } else {
                values[i] = null;
            }
        }
        return values;
    }

    public String[] getRemoteValues(final String oid, final String[] idxs, int maxsize) {
        String[] oids = new String[idxs.length];
        for (int i = 0; i < idxs.length; i++) {
            oids[i] = oid + "." + idxs[i];
        }
        return getRemoteValues(oids, maxsize);
    }

    public String set(String oid, int value) {
        PDU pdu = new PDU();
        pdu.setType(PDU.SET);
        pdu.add(new VariableBinding(new OID(oid), new Integer32(value)));
        pdu = send(pdu);
        if (pdu != null && pdu.size() > 0) {
            String val = pdu.get(0).getVariable().toString().trim();
            logger.log(Level.FINE, "发送：{0};接收到：{1}", new Object[]{oid, val});
            return val;
            //return pdu.get(0).getVariable().toString();
        } else {
            return "";
        }
    }

    public String[] set(String[] oids, Object[] values) {
        PDU pdu = new PDU();
        pdu.setType(PDU.SET);
        for (int i = 0; i < oids.length; i++) {
            if (values[i] instanceof Integer) {
                int val = Integer.parseInt(values[i].toString());
                pdu.add(new VariableBinding(new OID(oids[i]), new Integer32(val)));
            } else {
                pdu.add(new VariableBinding(new OID(oids[i]), new OctetString(values[i].toString())));
            }
            logger.log(Level.FINE, "SET:发送：{0} 设置值:{1};", new Object[]{oids[i], values[i]});
        }
        PDU recv_pdu = send(pdu);
        if (recv_pdu != null && recv_pdu.size() > 0) {
            String[] ress = new String[recv_pdu.size()];
            for (int i = 0; i < ress.length; i++) {
                ress[i] = recv_pdu.get(i).getVariable().toString();
                logger.log(Level.FINE, "SET:接收到：{0}", new Object[]{ress[i]});
            }
            return ress;
        } else {
            logger.log(Level.FINE, "没有接收到数据");
            return new String[0];
        }
    }

    public String set(String oid, String value) {
        PDU pdu = new PDU();
        pdu.setType(PDU.SET);
        pdu.add(new VariableBinding(new OID(oid), new OctetString(value)));
        logger.log(Level.FINE, "SET:发送：{0} 设置值:{1};", new Object[]{oid, value});
        PDU recv_pdu = send(pdu);
        if (recv_pdu != null && recv_pdu.size() > 0) {
            String res = recv_pdu.get(0).getVariable().toString();
            logger.log(Level.FINE, "SET:接收到:{0};", new Object[]{res});
            return res;
        } else {
            logger.log(Level.FINE, "没有接收到数据");
            return "";
        }
    }

    /**
     *
     * @param oid
     * @return 返回Oid和值的对应Map
     */
    public LinkedHashMap<String, String> getMapUnderOid(final String oid) {
        PDU pdu = new PDU();
        pdu.setType(PDU.GETNEXT);
        OID toid = new OID(oid);
        pdu.add(new VariableBinding(toid));
        LinkedHashMap<String, String> mp = new LinkedHashMap();
        OID nextoid = null;
        do {
            if (pdu != null && pdu.size() > 0) {
                pdu.setType(PDU.GETNEXT);
                String sendoid = pdu.get(0).getOid().toString();
                String value = pdu.get(0).getVariable().toString();
                mp.put(sendoid, value);
                logger.log(Level.FINE, "GET NEXT:发送：{0} 收到:{1};", new Object[]{sendoid, value});
                pdu = send(pdu);
                if (pdu != null && pdu.get(0) != null) {
                    nextoid = pdu.get(0).getOid();
                    logger.log(Level.FINE, "GET NEXT:下一个OID:{0};", new Object[]{nextoid});
                } else {
                    break;
                }
            }
        } while (nextoid.startsWith(toid) && !nextoid.toString().equals(toid.toString()));
        HashSet<String> pdukeyset = new HashSet();
        for (Map.Entry<String, String> entry : mp.entrySet()) {
            String val = entry.getValue();
            if (val == null || val.isEmpty() || val.equals("Null")) {
                pdukeyset.add(entry.getKey());
            }
        }
        for (String key : pdukeyset) {
            mp.remove(key);
        }
        return mp;
    }

    public List<String> getListUnderOid(final String oid) {
        PDU pdu = new PDU();
        pdu.setType(PDU.GETNEXT);
        OID toid = new OID(oid);
        pdu.add(new VariableBinding(toid));
        List<String> values = new ArrayList();
        OID nextoid = null;
        String value = "0";//保证value的值不是empty而随便设的值
        do {
            if (pdu != null && pdu.size() > 0) {
                pdu.setType(PDU.GETNEXT);
                value = pdu.get(0).getVariable().toString();
                if (value != null && !value.isEmpty() && !value.equals("Null")) {
                    values.add(value);
                    logger.log(Level.FINE, "GET NEXT:发送：{0} 收到:{1};", new Object[]{pdu.get(0).getOid(), value});
                }
                pdu = send(pdu);
                nextoid = pdu.get(0).getOid();
                logger.log(Level.FINE, "GET NEXT:下一个OID:{0};", new Object[]{nextoid});
            }
        } while (nextoid.startsWith(toid) && !nextoid.toString().equals(toid.toString()));
        return values;
    }

    /**
     * @return
     */
    public String getIpAddress() {
        return this.ip;
    }

    public int getPort() {
        return this.port;
    }

    public String[] getRemoteValues(String[] oids, String ifidx, int maxsize) {
        String[] sendoids = new String[oids.length];
        for (int i = 0; i < sendoids.length; i++) {
            sendoids[i] = oids[i] + "." + ifidx;
        }
        return getRemoteValues(sendoids, maxsize);
    }
}
